#pragma once

#ifdef GK_DEVICE_DIRECTX9

namespace gk
{
	namespace TypeConversion
	{
		inline BufferFormat::Enum ConvertBufferFormat(D3DFORMAT bufferFormat)
		{
			switch (bufferFormat)
			{
			case D3DFMT_VERTEXDATA:			return BufferFormat::VertexStream;
			case D3DFMT_INDEX16:			return BufferFormat::Index16Stream;
			case D3DFMT_INDEX32:			return BufferFormat::Index32Stream;

			case D3DFMT_X8R8G8B8:
			case D3DFMT_A8R8G8B8:			return BufferFormat::R8G8B8A8;
			case D3DFMT_R8G8B8:				return BufferFormat::R8G8B8;
			case D3DFMT_R5G6B5:				return BufferFormat::R5G6B5;
			case D3DFMT_A16B16G16R16F:		return BufferFormat::RGBA16F;
			case D3DFMT_A32B32G32R32F:		return BufferFormat::RGBA32F;

			case D3DFMT_DXT1:				return BufferFormat::DXT1;
			case D3DFMT_DXT2:				return BufferFormat::DXT2;
			case D3DFMT_DXT3:				return BufferFormat::DXT3;
			case D3DFMT_DXT4:				return BufferFormat::DXT4;
			case D3DFMT_DXT5:				return BufferFormat::DXT5;

			case D3DFMT_D16:				return BufferFormat::D16;
			case D3DFMT_D24S8:				return BufferFormat::D24S8;
			case D3DFMT_D32:				return BufferFormat::D32;

			default:						CR_ERROR_STRING("Unknown buffer format specified"); return BufferFormat::Invalid;
			}
		}

		inline D3DFORMAT ConvertBufferFormat(BufferFormat::Enum bufferFormat)
		{
			switch (bufferFormat)
			{
			case BufferFormat::VertexStream:		return D3DFMT_VERTEXDATA;
			case BufferFormat::Index16Stream:		return D3DFMT_INDEX16;
			case BufferFormat::Index32Stream:		return D3DFMT_INDEX32;
			case BufferFormat::ConstantBuffer:		CR_ERROR_STRING("D3D9 requires constant buffer support"); return D3DFMT_FORCE_DWORD;

			case BufferFormat::R8G8B8A8:			return D3DFMT_A8R8G8B8;
			case BufferFormat::R8G8B8:				return D3DFMT_R8G8B8;
			case BufferFormat::R5G6B5:				return D3DFMT_R5G6B5;
			case BufferFormat::RGBA16F:				return D3DFMT_A16B16G16R16F;
			case BufferFormat::RGBA32F:				return D3DFMT_A32B32G32R32F;

			case BufferFormat::DXT1:				return D3DFMT_DXT1;
			case BufferFormat::DXT2:				return D3DFMT_DXT2;
			case BufferFormat::DXT3:				return D3DFMT_DXT3;
			case BufferFormat::DXT4:				return D3DFMT_DXT4;
			case BufferFormat::DXT5:				return D3DFMT_DXT5;

			case BufferFormat::D16:					return D3DFMT_D16;
			case BufferFormat::D24S8:				return D3DFMT_D24S8;
			case BufferFormat::D32:					return D3DFMT_D32;

			default:								CR_ERROR_STRING("Unknown buffer format specified"); return D3DFMT_FORCE_DWORD;
			}
		}

		inline D3DDECLUSAGE ConvertSemantic(cr::HashedString hsSemantic)
		{
			switch (hsSemantic.GetHash())
			{
			case shared::prehash::hs_POSITION:		return D3DDECLUSAGE_POSITION;
			case shared::prehash::hs_BLENDWEIGHT:	return D3DDECLUSAGE_BLENDWEIGHT;
			case shared::prehash::hs_BLENDINDICES:	return D3DDECLUSAGE_BLENDINDICES;
			case shared::prehash::hs_NORMAL:		return D3DDECLUSAGE_NORMAL;
			case shared::prehash::hs_PSIZE:			return D3DDECLUSAGE_PSIZE;
			case shared::prehash::hs_TEXCOORD:		return D3DDECLUSAGE_TEXCOORD;
			case shared::prehash::hs_TANGENT:		return D3DDECLUSAGE_TANGENT;
			case shared::prehash::hs_BINORMAL:		return D3DDECLUSAGE_BINORMAL;
			case shared::prehash::hs_TESSFACTOR:	return D3DDECLUSAGE_TESSFACTOR;
			case shared::prehash::hs_POSITIONT:		return D3DDECLUSAGE_POSITIONT;
			case shared::prehash::hs_COLOUR:		return D3DDECLUSAGE_COLOR;
			case shared::prehash::hs_FOG:			return D3DDECLUSAGE_FOG;
			case shared::prehash::hs_DEPTH:			return D3DDECLUSAGE_DEPTH;
			case shared::prehash::hs_SAMPLE:		return D3DDECLUSAGE_SAMPLE;

			default:						CR_ERROR_STRING("Unknown semantic encountered"); return (D3DDECLUSAGE)-1;
			}
		}

		inline D3DDECLTYPE ConvertType(cr::HashedString hsType)
		{
			switch (hsType.GetHash())
			{
			case shared::prehash::hs_Float1:		return D3DDECLTYPE_FLOAT1;
			case shared::prehash::hs_Float2:		return D3DDECLTYPE_FLOAT2;
			case shared::prehash::hs_Float3:		return D3DDECLTYPE_FLOAT3;
			case shared::prehash::hs_Float4:		return D3DDECLTYPE_FLOAT4;
			case shared::prehash::hs_UByte4:		return D3DDECLTYPE_UBYTE4;
			case shared::prehash::hs_Short2:		return D3DDECLTYPE_SHORT2;
			case shared::prehash::hs_Short4:		return D3DDECLTYPE_SHORT4;
			case shared::prehash::hs_UByte4N:		return D3DDECLTYPE_UBYTE4N;
			case shared::prehash::hs_Short2N:		return D3DDECLTYPE_SHORT2N;
			case shared::prehash::hs_Short4N:		return D3DDECLTYPE_SHORT4N;
			case shared::prehash::hs_UShort2N:		return D3DDECLTYPE_USHORT2N;
			case shared::prehash::hs_UShort4N:		return D3DDECLTYPE_USHORT4N;

			default:						CR_ERROR_STRING("Unknown declaration type encountered"); return (D3DDECLTYPE)-1;
			}
		}

		inline D3DPRIMITIVETYPE ConvertPrimitiveType(PrimitiveType::Enum primType)
		{
			switch (primType)
			{
			case PrimitiveType::TriangleList:	return D3DPT_TRIANGLELIST;
			default:							CR_ERROR_STRING("Unknown primitive type encountered"); return D3DPT_FORCE_DWORD;
			}
		}

		inline GpuProgramParameterType::Enum ConvertGpuProgramParameterType(cr::HashedString paramType)
		{
			switch (paramType)
			{
			case shared::prehash::hs_FLOAT:			return GpuProgramParameterType::Float;
			case shared::prehash::hs_INT:			return GpuProgramParameterType::Int;
			case shared::prehash::hs_BOOL:			return GpuProgramParameterType::Bool;
			case shared::prehash::hs_TEXTURE:		
			case shared::prehash::hs_TEXTURE1D:		
			case shared::prehash::hs_TEXTURE2D:		
			case shared::prehash::hs_TEXTURE3D:		
			case shared::prehash::hs_TEXTURECUBE:	return GpuProgramParameterType::Texture;
			case shared::prehash::hs_SAMPLER:		
			case shared::prehash::hs_SAMPLER1D:		
			case shared::prehash::hs_SAMPLER2D:		
			case shared::prehash::hs_SAMPLER3D:		
			case shared::prehash::hs_SAMPLERCUBE:	return GpuProgramParameterType::Sampler;
			}

			CR_ERROR_STRING("Unsupported parameter type"); 
			return GpuProgramParameterType::Invalid;
		}

		inline void ConvertRenderStateValue(RenderStateValue::Enum value, DWORD& outValue)
		{
			switch (value)
			{
			case RenderStateValue::DepthTestEnabled:	outValue = D3DZB_TRUE;		break;
			case RenderStateValue::DepthTestDisabled:	outValue = D3DZB_FALSE;		break;
			case RenderStateValue::DepthWriteEnabled:	outValue = TRUE;			break;
			case RenderStateValue::DepthWriteDisabled:	outValue = FALSE;			break;
			case RenderStateValue::AlphaTestEnabled:	outValue = TRUE;			break;
			case RenderStateValue::AlphaTestDisabled:	outValue = FALSE;			break;
			case RenderStateValue::CullFaceNone:		outValue = D3DCULL_NONE;	break;
			case RenderStateValue::CullFaceBack:		outValue = D3DCULL_CCW;		break;
			case RenderStateValue::CullFaceFront:		outValue = D3DCULL_CW;		break;
			default:									CR_ERROR_STRING("Unknown renderstate value encountered");
			}
		}

		inline void ConvertRenderState(RenderState::Enum renderState, const StateBlock::StateValue& val, D3DRENDERSTATETYPE& outRenderStateType, DWORD& outValue)
		{
			switch (renderState)
			{
			case RenderState::AlphaTest:			outRenderStateType = D3DRS_ALPHATESTENABLE; ConvertRenderStateValue(val.m_renderStateValue, outValue);	break;
			case RenderState::AlphaTestThreshold:	outRenderStateType = D3DRS_ALPHAREF;		outValue = val.m_intValue;									break;
			case RenderState::DepthTest:			outRenderStateType = D3DRS_ZENABLE;			ConvertRenderStateValue(val.m_renderStateValue, outValue);	break;
			case RenderState::DepthWrite:			outRenderStateType = D3DRS_ZWRITEENABLE;	ConvertRenderStateValue(val.m_renderStateValue, outValue);	break;
			case RenderState::CullFace:				outRenderStateType = D3DRS_CULLMODE;		ConvertRenderStateValue(val.m_renderStateValue, outValue);	break;
			default:								CR_ERROR_STRING("Unknown renderstate encountered");
			}
		}

		inline void ConvertSamplerStateValue(SamplerStateValue::Enum samplerStateValue, DWORD& outValue)
		{
			switch (samplerStateValue)
			{	
			case SamplerStateValue::FilterPoint:		outValue = D3DTEXF_POINT;		break;
			case SamplerStateValue::FilterLinear:		outValue = D3DTEXF_LINEAR;		break;
			case SamplerStateValue::FilterAnisotropic:	outValue = D3DTEXF_ANISOTROPIC;	break;
			case SamplerStateValue::AddressModeWrap:	outValue = D3DTADDRESS_WRAP;	break;
			case SamplerStateValue::AddressModeClamp:	outValue = D3DTADDRESS_CLAMP;	break;
			default:									CR_ERROR_STRING("Unknown sampler state value encountered");
			}
		}

		inline void ConvertSamplerState(SamplerState::Enum samplerState, const StateBlock::StateValue& val, D3DSAMPLERSTATETYPE& outSamplerStateType, DWORD& outValue)
		{	
			switch (samplerState)
			{
			case SamplerState::AddressModeU:	outSamplerStateType = D3DSAMP_ADDRESSU;			ConvertSamplerStateValue(val.m_samplerStateValue, outValue);	break;
			case SamplerState::AddressModeV:	outSamplerStateType = D3DSAMP_ADDRESSV;			ConvertSamplerStateValue(val.m_samplerStateValue, outValue);	break;
			case SamplerState::AddressModeW:	outSamplerStateType = D3DSAMP_ADDRESSW;			ConvertSamplerStateValue(val.m_samplerStateValue, outValue);	break;
			case SamplerState::FilterMin:		outSamplerStateType = D3DSAMP_MINFILTER;		ConvertSamplerStateValue(val.m_samplerStateValue, outValue);	break;
			case SamplerState::FilterMag:		outSamplerStateType = D3DSAMP_MAGFILTER;		ConvertSamplerStateValue(val.m_samplerStateValue, outValue);	break;
			case SamplerState::FilterMip:		outSamplerStateType = D3DSAMP_MIPFILTER;		ConvertSamplerStateValue(val.m_samplerStateValue, outValue);	break;
			case SamplerState::FilterMaxAnisos:	outSamplerStateType = D3DSAMP_MAXANISOTROPY;	outValue = val.m_intValue;										break;
			case SamplerState::FilterMipBias:	outSamplerStateType = D3DSAMP_MIPMAPLODBIAS;	outValue = *((DWORD*)&val.m_floatValue);						break;
			default:							CR_ERROR_STRING("Unknown sampler state encountered");
			}
		}
	}
}

#endif // GK_DEVteICE_DIRECTX9